#pragma once
struct Vector4;

// ================================================================================================
// Basic mathematic implementation of Matrix4x4
// ================================================================================================
struct Matrix4x4 {
    
    union {
        struct {
            float m00, m01, m02, m03;
            float m10, m11, m12, m13;
            float m20, m21, m22, m23;
            float m30, m31, m32, m33;
        };
        float m[4][4];
    };

    // -- default constructor
    Matrix4x4();
    
    // -- create a 4x4 matrix with 16 floats
    Matrix4x4(float, float, float, float,
              float, float, float, float,
              float, float, float, float,
              float, float, float, float);

    // -- create a 4x4 matrix with 4 row vectors
    Matrix4x4(Vector4*, Vector4*, Vector4*, Vector4*);

    // -- public interface
    Matrix4x4 Transpose();

    // -- mutator methods
    void SetRow1(const Vector4&);
    void SetRow1(float, float, float);
    void SetRow2(const Vector4&);
    void SetRow2(float, float, float);
    void SetRow3(const Vector4&);
    void SetRow3(float, float, float);
    void SetRow4(const Vector4&);
    void SetRow4(float, float, float);

    // -- accessor methods
    Vector4& GetRow1();
    Vector4& GetRow2();
    Vector4& GetRow3();
    Vector4& GetRow4();

    // -- operator overloads
    Matrix4x4 operator*(Matrix4x4& m);
    Matrix4x4 operator*=(Matrix4x4& m);
    Matrix4x4 operator+(Matrix4x4& m);
    Matrix4x4 operator+=(Matrix4x4& m);
};

// ================================================================================================
// Default Constructor
// ================================================================================================
inline Matrix4x4::Matrix4x4() {
    m00 = 0.0f;
    m01 = 0.0f;
    m02 = 0.0f;
    m03 = 0.0f;
    m10 = 0.0f;
    m11 = 0.0f;
    m12 = 0.0f;
    m13 = 0.0f;
    m20 = 0.0f;
    m21 = 0.0f;
    m22 = 0.0f;
    m23 = 0.0f;
    m30 = 0.0f;
    m31 = 0.0f;
    m32 = 0.0f;
    m33 = 0.0f;
}

inline Matrix4x4::Matrix4x4(float m00_, float m01_, float m02_, float m03_,
                            float m10_, float m11_, float m12_, float m13_,
              			    float m20_, float m21_, float m22_, float m23_,
              			    float m30_, float m31_, float m32_, float m33_) {
    m00 = m00_;
    m01 = m01_;
    m02 = m02_;
    m03 = m03_;
    m10 = m10_;
    m11 = m11_;
    m12 = m12_;
    m13 = m13_;
    m20 = m20_;
    m21 = m21_;
    m22 = m22_;
    m23 = m23_;
    m30 = m30_;
    m31 = m31_;
    m32 = m32_;
    m33 = m33_;

}

// ================================================================================================
// Mutator methods
// ================================================================================================
inline void Matrix4x4::SetRow1(float x, float y, float z) { 
    m00 = x;
    m01 = y;
    m02 = z;
}

inline void Matrix4x4::SetRow2(float x, float y, float z) { 
    m00 = x;
    m01 = y;
    m02 = z;
}

inline void Matrix4x4::SetRow3(float x, float y, float z) { 
    m00 = x;
    m01 = y;
    m02 = z;
}

inline void Matrix4x4::SetRow4(float x, float y, float z) { 
    m00 = x;
    m01 = y;
    m02 = z;
}

// ================================================================================================
// Accessor methods
// ================================================================================================
inline Vector4& Matrix4x4::GetRow1() {
    return *reinterpret_cast<Vector4*>(&m00);
}

inline Vector4& Matrix4x4::GetRow2() {
    return *reinterpret_cast<Vector4*>(&m10);
}

inline Vector4& Matrix4x4::GetRow3() {
    return *reinterpret_cast<Vector4*>(&m20);
}

inline Vector4& Matrix4x4::GetRow4() {
    return *reinterpret_cast<Vector4*>(&m30);
}

// ================================================================================================
// Return a simple 4x4 identity matrix
// ================================================================================================
inline Matrix4x4 Identity() { 
    Matrix4x4 temp;

    temp.m00 = 1.0f;
    temp.m11 = 1.0f;
    temp.m22 = 1.0f;
    temp.m33 = 1.0f;

    return temp;
}

